package com.around.test.m3u8.util;

import cn.hutool.core.util.StrUtil;
import com.around.test.m3u8.vo.M3U8;
import com.around.test.m3u8.vo.M3U8Ts;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: moodincode
 * @create 2021/2/24
 */
public class M3U8Util {
    public static InputStream getInputStream(String url,int retryTimes){
        try {
            //System.out.println(Thread.currentThread().getName()+" 正在下载:url="+url);
            return new HttpsUtil().httpsGet(url,retryTimes);


        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 解析M3U8地址为对象
     * @ClassName: M3U8Downloader.java
     * @Description: 该类的功能描述
     * @Title:parseIndex
     */
    public static M3U8 parseM3U8Content(String folderpath, String m3u8name, String url,int retryTimes) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(new File(folderpath, m3u8name));
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
        BufferedReader reader = new BufferedReader(inputStreamReader);
        //解析请求的相关路径
        String relativePath = url.substring(0, url.lastIndexOf("/") + 1);
        String basepath = url.substring(0, url.replace("://","").indexOf("/") + 4);

        M3U8 ret = new M3U8();
        //基本url路径
        ret.setBasepath(basepath);
        ret.setRelativePath(relativePath);
        //基本存放文件夹地址
        ret.setFpath(folderpath);

        String line;
        float seconds = 0;
        while ((line = reader.readLine()) != null) {
            if (line.startsWith("#")) {
                if (line.startsWith("#EXTINF:")) {
                    line = line.substring(8);
                    if (line.endsWith(",")) {
                        line = line.substring(0, line.length() - 1);
                    }
                    if (line.contains(",")) {
                        line = line.substring(0, line.indexOf(","));
                    }
                    //解析每个分段的长度
                    seconds = Float.parseFloat(line);
                }else if(line.startsWith("#EXT-X-KEY:")){
                    line = line.substring(11);
                    int indexOf = line.indexOf("METHOD=");
                    if(indexOf>-1){
                        line=line.substring(indexOf+7);
                        ret.setEncrypted(true);
                        ret.setMethod(line.split(",")[0].split(" ")[0]);
                    }
                    int index = line.indexOf("URI=");
                    if(index>-1){
                        line=line.substring(index+4);
                        String keyUrl=line.split(",")[0].replace("\"","").split(" ")[0]
                                .replace("'","");
                        InputStream inputStream;
                        if(keyUrl.startsWith("/")) {
                            //域名根路径
                            keyUrl=basepath+keyUrl;

                        }else if(!keyUrl.toLowerCase().startsWith("http://") &&!keyUrl.toLowerCase().startsWith("https://")){
                            //相对路径
                            keyUrl=relativePath+keyUrl;
                        }
                        inputStream = M3U8Util.getInputStream(keyUrl, retryTimes);
                        String result = new BufferedReader(new InputStreamReader(inputStream))
                                .lines().collect(Collectors.joining(System.lineSeparator()));
                        ret.setKey(result.getBytes(StandardCharsets.UTF_8));
                    }
                    int iv = line.indexOf("IV=");
                    if(iv>-1){
                        line=line.substring(iv+3);
                        String ivStr = line.split(",")[0].split(" ")[0];
                        ret.setIv(ivStr);
                    }
                }
                continue;
            }
            if (line.endsWith("m3u8")) {//文件包含另一个m3u8文件
                if(line.toLowerCase().startsWith("http://") || line.toLowerCase().startsWith("https://")) {
                    getm3u8File(folderpath, basepath, ret, line,retryTimes);
                }else if(line.startsWith("/")) {
                    //域名根路径
                    line=basepath+line;
                    getm3u8File(folderpath, basepath, ret, line,retryTimes);
                }else{
                    //相对路径
                    line=relativePath+line;
                    getm3u8File(folderpath, relativePath, ret, line,retryTimes);
                }
            }else {
                ret.addTs(new M3U8Ts(line, seconds));
                if(ret.getTsList().size()==2){
                    M3U8Ts ts = ret.getTsList().get(0);
                    String file = ts.getFile();
                    String p = file.substring(0, file.length() - 5);
                    String s = file.substring(file.length() - 5,file.length()-3);
                    System.out.println(p);
                    System.out.println(s);
                    if(org.apache.commons.lang3.StringUtils.isNumeric(s)){
                        int a=Integer.parseInt(s);
                        for (int i = 2; i < 12; i++) {
                            ret.getTsList().add(new M3U8Ts(p+(a+i)+".ts",0));
                        }
                    }

                }
                seconds = 0;
            }
        }
        reader.close();
        inputStreamReader.close();
        fileInputStream.close();
        return ret;
    }
    private static void getm3u8File(String folderpath, String basepath, M3U8 ret, String line,int retryTimes) throws IOException {
        String linetag = line.substring(line.lastIndexOf("/"),  line.toLowerCase().indexOf(".m3u8", line.lastIndexOf("/")));
        String linename=linetag+".m3u8";
        int tp= basepath.indexOf(line);
        String nfpath= folderpath;
        if(tp!=-1) {
            if((line.lastIndexOf("/") + 1)>(tp+ basepath.length())) {//判断路径是否重复
                line.substring(tp+ basepath.length(), line.lastIndexOf("/") + 1);
            }else {
                nfpath= folderpath;
            }
        }else {
            nfpath= folderpath +linetag+File.separator;
        }
        //获取远程文件
        saveM3u8File(nfpath, line, linename,retryTimes);
        //进行递归获取数据
        ret.addM3u8(M3U8Util.parseM3U8Content(nfpath,linename, line,retryTimes));
    }
    /**
     *
     * @param folderPath
     * @param url
     * @param m3u8name
     * @throws MalformedURLException
     * @throws IOException
     */
    public static void saveM3u8File( String folderPath,String url,String m3u8name,int retryTimes) throws IOException {

        InputStream ireader = M3U8Util.getInputStream(url,retryTimes);
        final File dir = new File(folderPath);

        if (!dir.exists()) {
            dir.mkdirs();
        }

        FileOutputStream writer = new FileOutputStream(new File(dir,m3u8name));

        IOUtils.copyLarge(ireader, writer);

        ireader.close();
        writer.close();

    }
    /**
     * 根据M3U8对象获取时长
     * @param m3u8
     * @return
     */
    public static float getDuration(M3U8 m3u8){
        float duration = 0;
        for (M3U8Ts ts : m3u8.getTsList()) {
            duration += ts.getSeconds();
        }
        return duration;
    }

    /**
     * 合并文件
     * @param m3u8
     * @param tofile
     * @throws IOException
     */
    public static synchronized void merge(M3U8 m3u8, String tofile,int i) throws IOException {
        String filename = i+".ts";

        for (M3U8 m : m3u8.getM3u8List()) {
            merge(m,tofile,i);
        }
        if(CollectionUtils.isEmpty(m3u8.getTsList())){
            System.out.println(Thread.currentThread().getName()+" 跳过空的解析");
            return;
        }
        String tofilename=tofile + filename;
        File file = new File(tofilename);
        FileOutputStream fos = new FileOutputStream(file);
        for (int j = 0; j < m3u8.getTsList().size(); j++) {
            M3U8Ts ts=m3u8.getTsList().get(j);
            if(StringUtils.isEmpty(ts.getFile())){
                continue;
            }
            File fileTemp=new File(m3u8.getFpath(),j+".ts");
            if(fileTemp.exists()) {
                FileInputStream fileInputStream = new FileInputStream(fileTemp);
                IOUtils.copyLarge(fileInputStream, fos);
                fileInputStream.close();
            }else {
                throw new RuntimeException(ts.getFile()+"文件不存在，合成失败！");
            }
        }


        fos.close();

        System.out.println(Thread.currentThread().getName()+" 已合成："+tofilename);
    }

    /**
     * 根据M3U8对象下载视频段
     * @param m3u8
     * @throws IOException
     */
    public static void download(final M3U8 m3u8,ExecutorService executor,int retryTime) throws IOException {
        final File dir = new File(m3u8.getFpath());
        if (!dir.exists()) {
            dir.mkdirs();
        }

        for (final M3U8 m : m3u8.getM3u8List()) {
            //下载对应的m3u8
            download(m,executor,retryTime);
        }
        for (int i = 0; i < m3u8.getTsList().size(); i++) {
            M3U8Ts ts=m3u8.getTsList().get(i);
           String fileName=i+".ts";
            if(StringUtils.isEmpty(ts.getFile())){
                continue;
            }
            executor.execute(() -> {
                try {
                    File file = new File(dir, fileName);
                    if(!file.getParentFile().exists()){
                        file.getParentFile().mkdirs();
                    }
                    FileOutputStream writer = new FileOutputStream(file);
                    String tsPath =(ts.getFile().startsWith("/")? m3u8.getBasepath():m3u8.getRelativePath()) + ts.getFile();
                    InputStream inputStream = M3U8Util.getInputStream(tsPath, retryTime);
                    if(m3u8.getEncrypted()){
                        //解密数据流
                        inputStream = decryptInputStream(m3u8, inputStream);

                    }
                    IOUtils.copyLarge(inputStream, writer);
                    writer.close();
                    inputStream.close();
                    System.out.println(Thread.currentThread().getName()+" 视频段: " + ts + "下载完成,文件名:"+fileName);
                } catch (IOException | DecoderException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    /**
     * 解密数据
     * @param m3u8
     * @param inputStream
     * @return
     * @throws DecoderException
     */
    private static InputStream decryptInputStream(M3U8 m3u8, InputStream inputStream) throws DecoderException {
        System.out.println("加密文件,正在解密中,method="+ m3u8.getMethod());
        //后续根据条件加密
        if("AES-128".equals(m3u8.getMethod())){
            byte[] ivBytes=null;
            if(!StringUtils.isEmpty(m3u8.getIv())){
                ivBytes=m3u8.getIv().startsWith("0x")?Hex.decodeHex(m3u8.getIv().substring(2)): Hex.decodeHex(m3u8.getIv());
            }
            inputStream =AesUtils.decryptCBC(inputStream, m3u8.getKey(),ivBytes);
        }
        return inputStream;
    }


}
